package com.mindskip.xzs.component;

import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.mindskip.xzs.exception.BusinessException;
import com.mindskip.xzs.viewmodel.admin.qywx.QywxAccessTokenResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 *  企业微信
 *  https://developer.work.weixin.qq.com/document/path/91039
 * </p>
 *
 * @author guoguangkai
 * @since 2022/9/13
 */
@Slf4j
@Component
public class QywxComponent {
    /**
     * 获取access_token是调用企业微信API接口的第一步，相当于创建了一个登录凭证，其它的业务API接口，都需要依赖于access_token来鉴权调用者身份。
     * 因此开发者，在使用业务接口前，要明确access_token的颁发来源，使用正确的access_token。
     */
    private static final String GET_TOKEN_URL = "https://qyapi.weixin.qq.com/cgi-bin/gettoken";
    private static final String SEND_QYWX_MSG_URL = "https://qyapi.weixin.qq.com/cgi-bin/message/send";

    /**
     * 企业ID
     * 每个企业都拥有唯一的corpid，获取此信息可在管理后台“我的企业”－“企业信息”下查看“企业ID”（需要有管理员权限）
     */
    @Value("${qywx.corpId}")
    private String corpId;

    /**
     * agentid
     * 每个应用都有唯一的agentid。在管理后台->“应用与小程序”->“应用”，点进某个应用，即可看到agentid。
     */
    @Value("${qywx.agentId}")
    private String agentId;

    /**
     * 自建应用secret
     * 在管理后台->“应用与小程序”->“应用”->“自建”，点进某个应用，即可看到。
     */
    @Value("${qywx.agentSecret}")
    private String agentSecret;

    /**
     * 小程序自身ID
     */
    @Value("${xcx.appid}")
    private String appid;

    @Resource
    private RedisTemplate<String, String> redisTemplate;


    private static final String ACCESS_TOKEN_REDIS_KEY = "qywx:access_token:";

    /**
     * 获取access_token
     * access_token是企业后台去企业微信的后台获取信息时的重要票据，由corpid和secret产生。所有接口在通信时都需要携带此信息用于验证接口的访问权限
     * 开发者需要缓存access_token，用于后续接口的调用（注意：不能频繁调用gettoken接口，否则会受到频率拦截）。当access_token失效或过期时，需要重新获取。
     * access_token的有效期通过返回的expires_in来传达，正常情况下为7200秒（2小时），有效期内重复获取返回相同结果，过期后获取会返回新的access_token。
     * 由于企业微信每个应用的access_token是彼此独立的，所以进行缓存时需要区分应用来进行存储。
     * access_token至少保留512字节的存储空间。
     * 企业微信可能会出于运营需要，提前使access_token失效，开发者应实现access_token失效时重新获取的逻辑。
     */
    public String getAccessToken() {
        String token = redisTemplate.opsForValue().get(ACCESS_TOKEN_REDIS_KEY + agentSecret);
        if (StringUtils.isNotEmpty(token)) {
            log.info("从缓存获取企业微信access_token");
            return token;
        }

        String response = HttpUtil.get(GET_TOKEN_URL, new HashMap<String, Object>(3) {
            private static final long serialVersionUID = 8176338237055182571L;

            {
                // 企业ID
                put("corpid", corpId);
                // 应用的凭证密钥，内建应用用应用密钥
                put("corpsecret", agentSecret);
            }
        });
        if (StringUtils.isBlank(response)) {
            log.error("远程调用企业微信接口获取access_token失败，http请求返回null");
            throw new BusinessException("与企业微信内部系统通信失败，请稍后重试");
        }
        QywxAccessTokenResult qywxAccessToken;
        try {
            qywxAccessToken = JSONObject.parseObject(response, QywxAccessTokenResult.class);
        } catch (Exception e) {
            log.error("远程调用企业微信接口获取access_token，json解析失败，返回原始内容：{}", response);
            throw new BusinessException("与企业微信内部系统通信失败，请稍后重试");
        }
        if (qywxAccessToken == null) {
            log.error("远程调用企业微信接口获取access_token失败，http请求返回null");
            throw new BusinessException("与企业微信内部系统通信失败，请稍后重试");
        }
        if (!Long.valueOf(0).equals(qywxAccessToken.getErrCode())) {
            log.error("远程调用企业微信接口获取access_token出错，返回code：{}，msg：{}", qywxAccessToken.getErrCode(), qywxAccessToken.getErrMsg());
            throw new BusinessException("与企业微信内部系统通信失败，请稍后重试");
        }

        // 有效期2小时
        redisTemplate.opsForValue().set(ACCESS_TOKEN_REDIS_KEY + agentSecret, qywxAccessToken.getAccessToken(), 2, TimeUnit.HOURS);
        log.info("远程调用企业微信接口获取access_token成功，并放入缓存");
        return qywxAccessToken.getAccessToken();
    }


    /**
     * 小程序通知消息
     * 返回示例：
     * {
     * "errcode" : 0, // 返回码
     * "errmsg" : "ok", // 对返回码的文本描述内容
     * "invaliduser" : "userid1|userid2", // 不合法的userid，不区分大小写，统一转为小写
     * "invalidparty" : "partyid1|partyid2", // 不合法的partyid
     * "invalidtag": "tagid1|tagid2", // 不合法的标签id
     * "unlicenseduser" : "userid3|userid4", // 没有基础接口许可(包含已过期)的userid
     * "msgid": "xxxx", // 消息id，用于撤回应用消息
     * "response_code": "xyzxyz" // 仅消息类型为“按钮交互型”，“投票选择型”和“多项选择型”的模板卡片消息返回，应用可使用response_code调用更新模版卡片消息接口，24小时内有效，且只能使用一次
     * }
     * 如果部分接收人无权限或不存在，发送仍然执行，但会返回无效的部分（即invaliduser或invalidparty或invalidtag或unlicenseduser），常见的原因是接收人不在应用的可见范围内。
     * 权限包含应用可见范围和基础接口权限(基础账号、互通账号均可)，unlicenseduser中的用户在应用可见范围内但没有基础接口权限。
     * 如果全部接收人无权限或不存在，则本次调用返回失败，errcode为81013。
     * 返回包中的userid，不区分大小写，统一转为小写
     */
    public Boolean sendMsg(Object msg) {
/*
        MiniProgramNotice miniProgramNotice = new MiniProgramNotice()
                .setTouser("GuoGuangKai")
                .setMsgtype(QywxMsgType.MINI_PROGRAM_NOTICE.getMsgtype())
                .setEnable_id_trans(0)
                .setEnable_duplicate_check(0)
                .setDuplicate_check_interval(1800)
                .setMiniprogram_notice(
                        new MiniProgramNotice.Notice()
                                .setAppid(appid)
                                .setPage("pages/index?userid=zhangsan&orderid=123123123")
                                .setTitle("玉溪医药月度考试通知")
                                .setDescription(LocalDateTime.now().format(DateTimeFormatter.ofPattern("MM月dd日 HH:mm")))
                                .setEmphasis_first_item(true)
                                .setContent_item(
                                        Arrays.asList(
                                                new MiniProgramNotice.Notice.KeyVal()
                                                        .setKey("考试内容")
                                                        .setValue("病理学"),
                                                new MiniProgramNotice.Notice.KeyVal()
                                                        .setKey("考试时间")
                                                        .setValue("2022年9月18日 09:00 - 18:00"),
                                                new MiniProgramNotice.Notice.KeyVal()
                                                        .setKey("考试类型")
                                                        .setValue("月度考试"),
                                                new MiniProgramNotice.Notice.KeyVal()
                                                        .setKey("考试类型")
                                                        .setValue("连锁经营部全体员工")
                                        )
                                )
                ); */

        /*TextCardNotice notice = new TextCardNotice()
                .setTouser("GuoGuangKai")
                .setMsgtype(QywxMsgType.TEXT_CARD.getMsgtype())
                .setAgentid(1000002L)
                .setEnable_id_trans(0)
                .setEnable_duplicate_check(0)
                .setDuplicate_check_interval(1800)
                .setTextcard(
                        new TextCardNotice.Notice()
                                .setTitle("考试取消通知")
                                .setUrl("url")
                                .setBtntxt("测试")
                                .setDescription(
                                        String.format(
                                                "<div class=\"gray\">%s</div> " +
                                                        "<div class=\"normal\">考试【%s】已取消</div>" +
                                                        "<div class=\"highlight\">试卷《%s》已下架，请知晓</div>"
                                                , LocalDateTime.now().format(DateTimeFormatter.ofPattern("MM月dd日 HH:mm"))
                                                , "exam.getName()"
                                                , "paper.getName()"
                                        )

                                )
                );*/

        /*MarkdownNotice guoGuangKai = new MarkdownNotice()
                .setTouser("GuoGuangKai")
                .setMsgtype(QywxMsgType.MARK_DOWN.getMsgtype())
                .setAgentid(1000002L)
                .setEnable_duplicate_check(0)
                .setDuplicate_check_interval(1800)
                .setMarkdown(
                        new MarkdownNotice.Notice()
                                .setContent(
                                        String.format(
                                                "考试取消提醒\n" +
                                                        ">考试名称：<font color=\"info\">%s</font> \n" +
                                                        ">下架试卷：`%s`\n" +
                                                        ">试卷类型：%s\n" +
                                                        "> \n" +
                                                        ">下架日期：<font color=\"warning\">2018年5月18日</font> \n"
                                                , "exam.getName()"
                                                , "paper.getName()"
                                                , "ExamTypeEnum.fromCode(paper.getExamType()).getName()"
                                        )
                                )
                );*/

        HttpResponse response = HttpUtil.createPost(SEND_QYWX_MSG_URL + "?access_token=" + getAccessToken())
                .body(JSON.toJSONString(
                        msg
                ))
                .execute();
        if (response.isOk()) {
            log.error("企业微信消息发送成功，msg：{}", JSON.toJSONString(response.body()));
            return true;
        }
        log.error("企业微信消息发送失败，msg：{}，result{}", JSON.toJSONString(msg), JSON.toJSONString(response.body()));
        return false;
    }
}
